package com.appspot.didit.client.tag;

import java.util.ArrayList;
import java.util.HashMap;
import com.appspot.didit.client.user.UserManager;
import com.appspot.didit.client.util.Util;
import com.appspot.didit.client.workitem.Workitem;
import com.appspot.didit.client.workitem.WorkitemList;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class TagManager {
	
	protected static TagManager theInstance;
	
	private final TagServiceAsync tagService = GWT.create(TagService.class);
	
	protected ArrayList<Tag> tags;
	
	public HashMap<String, Tag> getMyTagNameIndex() {
		return myTagNameIndex;
	}

	public void setMyTagNameIndex(HashMap<String, Tag> myTagNameIndex) {
		this.myTagNameIndex = myTagNameIndex;
	}

	protected HashMap<String, Tag> myTagNameIndex;
	
	protected HashMap<String, ArrayList<Tag>> otherTagNameIndex;
	
	public int MAX_FREQUENCY = 0;
	
	TagCloud tagCloud;
	 
	public TagCloud getTagCloud() {
		return tagCloud;
	}

	public void setTagCloud(TagCloud tagCloud) {
		this.tagCloud = tagCloud;
	}

	public ArrayList<Tag> getTags() {
		return tags;
	}

	public void setTags(ArrayList<Tag> tags) {
		this.tags = tags;
		for(Tag t : tags) {
        	if(t.getCreator_id().equals(UserManager.getInstance().getUserId())) {
        		myTagNameIndex.put(t.getTagname(), t);
        		int frequency = t.getItem_id().size();
        		if( frequency > MAX_FREQUENCY) {
        			MAX_FREQUENCY = frequency;
        		}
        		
        	} else {
        		//FIXME: otherTags
        	}
        }
        WorkitemList.getTable();
        tagCloud = new TagCloud(myTagNameIndex.values().toArray(new Tag[myTagNameIndex.size()]));
	}

	public void addTag(Tag tag) {
		this.tags.add(tag);
	}
	
	public void delTag(Tag tag) {
		this.tags.remove(tag);
	}
	
	private TagManager(){
		myTagNameIndex = new HashMap<String, Tag>();
		otherTagNameIndex = new HashMap<String, ArrayList<Tag>>();
	}
	
	
	public static TagManager getInstance() {
		if(theInstance == null) {
			theInstance = new TagManager();
		}
		return theInstance;
	}
	public void generateIndex() {
		
	}
	
	public ArrayList<Tag> getTagsFromString(String tagString) {
		return null;
	}

	@SuppressWarnings("unchecked")
	public void saveTags(String tagString, Workitem wi) {
		String[] splitTags = Util.splitStringInArray(tagString); 
		for(String t : splitTags) {
			if(myTagNameIndex.containsKey(t)) {
				final Tag tag = myTagNameIndex.get(t);
				tag.addItem(wi.getId());
				System.out.println(tag);
				tagService.update(tag, new AsyncCallback() {
                    public void onFailure(Throwable caught) {
                        GWT.log("Failed to update a Tag", caught);
                    }
                    public void onSuccess(Object result) {
                    	GWT.log("sucessfully updated a Tag", null);
                    	if(tag.getItem_id().size() > MAX_FREQUENCY) {
        					MAX_FREQUENCY++;
        					tagCloud.updateTags(myTagNameIndex.values().toArray(new Tag[myTagNameIndex.size()]));
        				} else {
        					tagCloud.updateTag(tag);
        				}
                    }		
                });
			} else {
				Long user_id = UserManager.getInstance().getUserId();
				Tag tag = new Tag(t, user_id, UserManager.getInstance().getUser().getUsername());
				tag.addItem(wi.getId());
				tag.addUser(user_id);
				if(tags == null) {
					tags = new ArrayList<Tag>();
				}
				tagService.save(tag, new AsyncCallback() {
                    public void onFailure(Throwable caught) {
                        GWT.log("Failed to save a Tag", caught);
                    }
                    public void onSuccess(Object result) {
                    	Tag tag = (Tag)result;
                    	tags.add(tag);
                    	myTagNameIndex.put((tag).getTagname(), (Tag)result);
                    	tagCloud.addTag(tag);
                    	//WorkitemManager.getInstance().getLastCreatedWorkitem().addTag((Tag)result);
                    	GWT.log("sucessfully saved a Tag", null);
                    }		
                });
			}
		}
	}

	@SuppressWarnings("unchecked")
	public void loadTags(Long user_id) {
		tagService.loadTags(user_id, new AsyncCallback() {
			public void onFailure(Throwable caught) {
				GWT.log("Failed to load tags", caught);
            }
                    
            public void onSuccess(Object result) {
                GWT.log("Successfully loaded tags", null);
                Long uid = UserManager.getInstance().getUserId(); 
                for(Tag t : (ArrayList<Tag>) result) {
                	if(t.getCreator_id().equals(uid)) {
                		myTagNameIndex.put(t.getTagname(), t);
                		int frequency = t.getItem_id().size();
                		if( frequency > MAX_FREQUENCY) {
                			MAX_FREQUENCY = frequency;
                		}
                		
                	} else {
                		if(otherTagNameIndex.containsKey(t.getTagname())) {
                			ArrayList<Tag> tags = otherTagNameIndex.get(t.getTagname());
                			tags.add(t);
                		} else {
                			ArrayList<Tag> tags = new ArrayList<Tag>();
                			tags.add(t);
                			otherTagNameIndex.put(t.getTagname(), tags);
                		}
                	}
                }
                WorkitemList.getTable();
                tagCloud = new TagCloud(myTagNameIndex.values().toArray(new Tag[myTagNameIndex.size()]));
            }
		});
		
	}

	public void updateTags(String oldTagString, String newTagString, Workitem wi) {
		String[] oldTags = Util.splitStringInArray(oldTagString);
		String[] newTags = Util.splitStringInArray(newTagString);
		ArrayList<String> addedTags = new ArrayList<String>();
		ArrayList<String> removedTags = new ArrayList<String>();
		removedTags = this.getDifference(oldTags, newTags);
		addedTags = this.getDifference(newTags, oldTags);
		for(String t : removedTags) {
			Tag tag = this.myTagNameIndex.get(t);
			tag.delItem(wi.getId());
			tagService.update(tag, new AsyncCallback() {
                public void onFailure(Throwable caught) {
                    GWT.log("Failed to update a Tag", caught);
                }
                public void onSuccess(Object result) {
                	GWT.log("sucessfully updated a Tag", null);
                }		
            });
		}
		
		for(String t : addedTags) {
			if(myTagNameIndex.containsKey(t)) {
				Tag tag = this.myTagNameIndex.get(t);
				tag.addItem(wi.getId());
				tagService.update(tag, new AsyncCallback() {
	                public void onFailure(Throwable caught) {
	                    GWT.log("Failed to update a Tag", caught);
	                }
	                public void onSuccess(Object result) {
	                	GWT.log("sucessfully updated a Tag", null);
	                }		
	            });
			} else {
				Tag tag = new Tag(t, UserManager.getInstance().getUserId(), UserManager.getInstance().getUser().getUsername());
				tag.addItem(wi.getId());
				tag.addUser(UserManager.getInstance().getUserId());
				if(tags == null) {
					tags = new ArrayList<Tag>();
				}
				tagService.save(tag, new AsyncCallback() {
                    public void onFailure(Throwable caught) {
                        GWT.log("Failed to save a Tag", caught);
                    }
                    public void onSuccess(Object result) {
                    	tags.add((Tag)result);
                    	myTagNameIndex.put(((Tag)result).getTagname(), (Tag)result);
                    	GWT.log("sucessfully saved a Tag", null);
                    }		
                });
			}
		}
		
		/*for(Tag t : wi.getTags()) {
			boolean deleted = true;
			for(String st : splitTags) {
				if(t.getTagname().equals(st)) {
					deleted = false;
				}
			}
			if(deleted) {
				wi.delTag(t);
				t.delItem(wi.getId());
				tagService.update(t, new AsyncCallback() {
                    public void onFailure(Throwable caught) {
                        GWT.log("Failed to update a Tag", caught);
                    }
                    public void onSuccess(Object result) {
                    	GWT.log("sucessfully updated a Tag", null);
                    }		
                });
			}
		}
		
		for(String st : splitTags) {
			boolean added = true;
			for(Tag t : wi.getTags()) {
				if(t.getTagname().equals(st)) {
					added = false;
				}
			}
			if(added) {
				if(myTagNameIndex.containsKey(st)) {
					Tag tag = myTagNameIndex.get(st);
					tag.addItem(wi.getId());
					tagService.update(tag, new AsyncCallback() {
	                    public void onFailure(Throwable caught) {
	                        GWT.log("Failed to update a Tag", caught);
	                    }
	                    public void onSuccess(Object result) {
	                    	GWT.log("sucessfully updated a Tag", null);
	                    }		
	                });
					wi.addTag(tag);
				} else {
					Tag tag = new Tag(st, UserManager.getInstance().getUserId());
					tag.addItem(wi.getId());
					tag.addUser(UserManager.getInstance().getUserId());
					if(tags == null) {
						tags = new ArrayList<Tag>();
					}
					tagService.save(tag, new AsyncCallback() {
	                    public void onFailure(Throwable caught) {
	                        GWT.log("Failed to save a Tag", caught);
	                    }
	                    public void onSuccess(Object result) {
	                    	tags.add((Tag)result);
	                    	myTagNameIndex.put(((Tag)result).getTagname(), (Tag)result);
	                    	WorkitemManager.getInstance().getLastCreatedWorkitem().addTag((Tag)result);
	                    	GWT.log("sucessfully saved a Tag", null);
	                    }		
	                });
				}
			}
		}*/
		
	}
	
	private ArrayList<String> getDifference(String[] tags1, String[] tags2) {
		ArrayList<String> difference = new ArrayList<String>();
		for(String t1 : tags1) {
			boolean exist = false;
			for(String t2 : tags2) {
				if(t1.equals(t2)) {
					exist = true;
				}
			}
			if(!exist) {
				difference.add(t1);
			}
		}
		return difference;
	}
	
	public ArrayList<Long> getTag_ids(String tagString) {
		ArrayList<Long> tag_ids = new ArrayList<Long>();
		String[] splitTags = Util.splitStringInArray(tagString); 
		for(String t : splitTags) {
			Tag tag = myTagNameIndex.get(t);
			tag_ids.add(tag.getId());
		}
		return tag_ids;
	}
	
	public String getAllProjectsAsString() {
		String result = "";
		for(String t : myTagNameIndex.keySet()) {
			result += t + " ";
		}
		int length = result.length();
		result = result.substring(0, length - 1);
		return result;
	}
}